package com.castle.fortress.admin.ai.controler;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.castle.fortress.admin.ai.model.dto.GenerateQuestionByTitle;
import com.castle.fortress.admin.ai.model.dto.OuterResourceRequestBody;
import com.castle.fortress.admin.ai.model.enity.CastleConfigAiPrompt;
import com.castle.fortress.admin.ai.model.enity.QwenMessage;
import com.castle.fortress.admin.ai.model.enity.QwenRequestBody;
import com.castle.fortress.admin.ai.model.enums.AIAssiantEnum;
import com.castle.fortress.admin.ai.service.CastleConfigAiPromptService;
import com.castle.fortress.admin.ai.util.AIAPIUtil;
import com.castle.fortress.admin.dify.util.DifyUtil;
import com.castle.fortress.admin.member.dto.MemberDto;
import com.castle.fortress.admin.schedule.model.entity.StudySchedule;
import com.castle.fortress.admin.service.StudyScheduleService;
import com.castle.fortress.admin.study.course.dto.StudyCourseDto;
import com.castle.fortress.admin.study.course.service.StudyCourseService;
import com.castle.fortress.admin.study.exam.dto.RateResponseBody;
import com.castle.fortress.admin.study.exam.dto.StudyExamDto;
import com.castle.fortress.admin.study.exam.dto.StudyExamStatisticsMemberDto;
import com.castle.fortress.admin.study.exam.dto.StudyMemberExamDto;
import com.castle.fortress.admin.study.exam.entity.StudyMemberExamEntity;
import com.castle.fortress.admin.study.exam.service.StudyExamService;
import com.castle.fortress.admin.study.exam.service.StudyExamStatisticsMemberService;
import com.castle.fortress.admin.study.exam.service.StudyMemberExamService;
import com.castle.fortress.admin.utils.RedisUtils;
import com.castle.fortress.admin.utils.WebUtil;
import com.castle.fortress.common.entity.RespBody;
import com.castle.fortress.common.enums.CourseStatusEnum;
import com.castle.fortress.common.exception.BizException;
import com.castle.fortress.common.respcode.GlobalRespCode;
import com.castle.fortress.common.utils.ConvertUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * hello demo
 *
 * @author castle
 */
@Api(tags = "AI相关api")
@RestController
@Slf4j
public class AiController {
    @Resource
    private StudyCourseService studyCourseService;
    @Resource
    private CastleConfigAiPromptService castleConfigAiPromptService;
    @Resource
    StudyExamStatisticsMemberService studyExamStatisticsMemberService;
    @Resource
    StudyMemberExamService studyMemberExamService;
    @Resource
    StudyScheduleService studyScheduleService;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    StudyExamService studyExamService;
    @Resource
    DifyUtil difyUtil;
    @Resource
    RedisUtils redisUtils;
    final String MODEL = "qwen-plus";

    @ApiOperation("根据课程列表生成推荐课程")
    @PostMapping("/ai/recommend/course")
    public RespBody<JSONObject> recommendCourse() {
        MemberDto member = WebUtil.currentMember();
        if (member == null) {
            throw new BizException(GlobalRespCode.NO_LOGIN_ERROR);
        }
        QwenRequestBody requestBody = new QwenRequestBody();
        requestBody.setModel(MODEL);
        List<StudyCourseDto> list = studyCourseService.scheduleList(null, member);
        String scheduleList = JSONUtil.toJsonStr(list);
        CastleConfigAiPrompt byEnum = castleConfigAiPromptService.getByEnum(AIAssiantEnum.RECOMMEND_COURSE);
        String content = byEnum.getContent();
        int currentPage = 1;
        int size = 30;
        Page<StudyCourseDto> page = new Page<>(currentPage, size);
        StudyCourseDto studyCourseDto = new StudyCourseDto();
        studyCourseDto.setCourseStatus(CourseStatusEnum.PUTAWAY.getCode());
        IPage<StudyCourseDto> pages = studyCourseService.pageStudyCourseExtends(page, studyCourseDto);
        List<StudyCourseDto> records = pages.getRecords();
        String allCourse = JSONUtil.toJsonStr(records);
        content = content.replace("#{Schedulelist}", scheduleList).replace("{Studycourse}", allCourse);
        QwenMessage qwenMessage = new QwenMessage();
        qwenMessage.setRole("user");
        qwenMessage.setContent(content);
        requestBody.setMessages(Collections.singletonList(qwenMessage));
        String res = AIAPIUtil.recommendCourse(JSONUtil.toJsonStr(requestBody));
        return RespBody.data(JSONUtil.parseObj(res));
    }

    @ApiOperation("生成advice列表")
    @GetMapping("/ai/xft/advice/list")
    public RespBody<JSONArray> getAdvice() {
        MemberDto member = WebUtil.currentMember();
        if (member == null) {
            throw new BizException(GlobalRespCode.NO_LOGIN_ERROR);
        }

        Page<StudyExamDto> page = new Page<>(1, 10);
        IPage<StudyExamDto> pages = studyExamService.answeredExams(page, member.getId());
        IPage<StudyExamDto> endTheExams = studyExamService.endTheExams(page, member.getId());
        List<StudyExamDto> records = pages.getRecords();
        List<StudyExamDto> endTheExamsPages = endTheExams.getRecords();
        List<RateResponseBody> bodies = new ArrayList<>();
        endTheExamsPages.forEach(it -> {
            double memberScore = it.getMemberScore();
            double totalScore = it.getTotalScore();
            RateResponseBody body = new RateResponseBody();
            body.setName(it.getExamName());
            body.setRate((memberScore / totalScore) * 100);
            bodies.add(body);
        });
        records.forEach(it -> {
            double memberScore = it.getMemberScore();
            double totalScore = it.getTotalScore();
            RateResponseBody body = new RateResponseBody();
            body.setName(it.getExamName());
            body.setRate((memberScore / totalScore) * 100);
            bodies.add(body);
        });

        QwenRequestBody requestBody = new QwenRequestBody();
        requestBody.setModel(MODEL);
        String examList = JSONUtil.toJsonStr(bodies);
        CastleConfigAiPrompt byEnum = castleConfigAiPromptService.getByEnum(AIAssiantEnum.ADVICE_LIST);
        String content = byEnum.getContent();
        content = content.replace("#{examList}", examList);
        QwenMessage qwenMessage = new QwenMessage();
        qwenMessage.setRole("user");
        qwenMessage.setContent(content);
        requestBody.setMessages(Collections.singletonList(qwenMessage));
        String res = AIAPIUtil.getStr(JSONUtil.toJsonStr(requestBody));
        try {
            JSONArray jsonArray = JSONUtil.parseArray(res);
            return RespBody.data(jsonArray);
        } catch (Exception e) {
            log.error("请求结果：{}", res);
            return RespBody.data(JSONUtil.createArray().put(e.getMessage()));
        }
    }

    @ApiOperation("生成建议")
    @GetMapping("/ai/xft/memberExam/analyze/{id}")
    public RespBody<JSONObject> getAnalyze(@PathVariable("id") Long id) {
        MemberDto member = WebUtil.currentMember();
        if (member == null) {
            throw new BizException(GlobalRespCode.NO_LOGIN_ERROR);
        }

        if (id == null) {
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }

        StudyExamStatisticsMemberDto statisticsMemberDTO = studyExamStatisticsMemberService.queryStatisticeMemberInfo(id);
        QueryWrapper<StudyMemberExamEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("record_id", id);
        List<StudyMemberExamDto> examDTOS = ConvertUtil.transformObjList(studyMemberExamService.list(wrapper), StudyMemberExamDto.class);
        statisticsMemberDTO.setExamQuestionDTOList(examDTOS);

        QwenRequestBody requestBody = new QwenRequestBody();
        requestBody.setModel(MODEL);

        Integer score = statisticsMemberDTO.getScore();
        Integer totalScore = Integer.valueOf(statisticsMemberDTO.getTotalScore());

        JSONObject jsonObject = new JSONObject();
        List<String> weakList = new ArrayList<>();
        if (score >= totalScore) {
            jsonObject.set("list",weakList);
            jsonObject.set("advice", "非常棒");
            return RespBody.data(jsonObject);
        }

        String list = JSONUtil.toJsonStr(statisticsMemberDTO);
        CastleConfigAiPrompt byEnum = castleConfigAiPromptService.getByEnum(AIAssiantEnum.ANALYZE);
        String content = byEnum.getContent();
        content = content.replace("#{list}", list);
        QwenMessage qwenMessage = new QwenMessage();
        qwenMessage.setRole("user");
        qwenMessage.setContent(content);
        requestBody.setMessages(Collections.singletonList(qwenMessage));
        String res = AIAPIUtil.getStr(JSONUtil.toJsonStr(requestBody));
        jsonObject = JSONUtil.parseObj(res);
        try {
            return RespBody.data(jsonObject);
        } catch (Exception e) {
            log.error("请求结果：{}", jsonObject);
            return RespBody.fail(e.getMessage());
        }
    }

    @ApiOperation("根据题目列表生成试卷")
    @GetMapping("/ai/generate/question/{id}")
    public RespBody generateQuestion(@PathVariable("id") Long recordId) {
        MemberDto member = WebUtil.currentMember();
        if (member == null) {
            throw new BizException(GlobalRespCode.NO_LOGIN_ERROR);
        }
        if (recordId == null || recordId.equals(0)) {
            throw new BizException(GlobalRespCode.PARAM_MISSED);
        }
        StudyExamStatisticsMemberDto statisticsMemberDTO = studyExamStatisticsMemberService.queryStatisticeMemberInfo(recordId);
        QueryWrapper<StudyMemberExamEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("record_id", recordId);
        List<StudyMemberExamDto> examDTOS = ConvertUtil.transformObjList(studyMemberExamService.list(wrapper), StudyMemberExamDto.class);
        statisticsMemberDTO.setExamQuestionDTOList(examDTOS);
        String jsonStr = JSONUtil.toJsonStr(examDTOS);
        CastleConfigAiPrompt byEnum = castleConfigAiPromptService.getByEnum(AIAssiantEnum.GENERATE_QUESTIONS);
        String content = byEnum.getContent();
        content = content.replace("#{userPrompt}", jsonStr);
        QwenRequestBody qwenRequestBody = new QwenRequestBody();
        qwenRequestBody.setModel(MODEL);
        // qwenRequestBody.setMessages();
        QwenMessage qwenMessage = new QwenMessage();
        qwenMessage.setRole("user");
        qwenMessage.setContent(content);
        qwenRequestBody.setMessages(Collections.singletonList(qwenMessage));
        String res = AIAPIUtil.generateQuestion(JSONUtil.toJsonStr(qwenRequestBody));
        return RespBody.data(res);
    }


    @ApiOperation("根据课程生成问题")
    @PostMapping("/ai/generate/question/course")
    public RespBody generateQuestionByCourse(@RequestBody GenerateQuestionByTitle titleAndCount) {
        String title = titleAndCount.getTitle();
        Integer count = titleAndCount.getCount();
        final String key = "questions:title:" + title.hashCode() + ":count:" + count;
        log.info("key:" + key);
        if (stringRedisTemplate.hasKey(key)) {
            log.info("redis has");
            return RespBody.data(JSONUtil.parseArray(stringRedisTemplate.opsForValue().get(key)));
        }
        if (StrUtil.isBlank(title) || title.length() > 30 || count == null) {
            throw new BizException(GlobalRespCode.PARAM_MISSED, "title和count不能为空");
        }
        if (count < 1 || count > 30) {
            throw new BizException(GlobalRespCode.PARAM_MISSED, "count不能小于1或者大于30");
        }
        MemberDto member = WebUtil.currentMember();
        if (member == null) {
            throw new BizException(GlobalRespCode.NO_LOGIN_ERROR);
        }
        CastleConfigAiPrompt byEnum = castleConfigAiPromptService.getByEnum(AIAssiantEnum.GENERATE_QUESTION_BY_COURSE_TITLE);
        String content = byEnum.getContent();
        content = content.replace("#{title}", title).replace("#{count}", count.toString());
        QwenRequestBody qwenRequestBody = new QwenRequestBody();
        qwenRequestBody.setModel(MODEL);
        // qwenRequestBody.setMessages();
        QwenMessage qwenMessage = new QwenMessage();
        qwenMessage.setRole("user");
        qwenMessage.setContent(content);
        qwenRequestBody.setMessages(Collections.singletonList(qwenMessage));
        String res = AIAPIUtil.getString(JSONUtil.toJsonStr(qwenRequestBody));
        stringRedisTemplate.opsForValue().set(key, res);
        stringRedisTemplate.expire(key, 1, TimeUnit.DAYS);
        JSONArray array = JSONUtil.parseArray(res);
        return RespBody.data(array);
    }

    @ApiOperation("生成日程列表")
    @GetMapping("/ai/generate/schedule")
    public RespBody generateQuestion() {
        MemberDto member = WebUtil.currentMember();
        Long userId = member.getId();
        Date now = new Date();
        SimpleDateFormat dateFormat = DateUtil.newSimpleFormat("yyyy-MM-dd");
        List<StudySchedule> allSchedules = studyScheduleService.listMySchedule(userId);
        String schedulesTitleArrayStr = JSONUtil.toJsonStr(allSchedules.stream().limit(94).map(StudySchedule::getTitle).collect(Collectors.toList()));
        String scheduleReason = getReason(schedulesTitleArrayStr);

        for (StudySchedule schedule : allSchedules) {
            boolean equals = dateFormat.format(now).equals(dateFormat.format(schedule.getTaskTime()));
            if (equals) {
                return RespBody.data(scheduleReason, "今日已生成过，请勿重复提交");
            }
        }

        QwenRequestBody requestBody = new QwenRequestBody();
        requestBody.setModel(MODEL);
        List<StudyCourseDto> list = studyCourseService.scheduleList(null, member);
        String scheduleList = JSONUtil.toJsonStr(list);
        CastleConfigAiPrompt byEnum = castleConfigAiPromptService.getByEnum(AIAssiantEnum.GENERATE_SCHEDULE);
        String content = byEnum.getContent();
        int currentPage = 1;
        int size = 30;
        Page<StudyCourseDto> page = new Page<>(currentPage, size);
        StudyCourseDto studyCourseDto = new StudyCourseDto();
        studyCourseDto.setCourseStatus(CourseStatusEnum.PUTAWAY.getCode());
        IPage<StudyCourseDto> pages = studyCourseService.pageStudyCourseExtends(page, studyCourseDto);
        List<StudyCourseDto> records = pages.getRecords();
        String allCourse = JSONUtil.toJsonStr(records);
        content = content.replace("#{Schedulelist}", scheduleList).replace("{Studycourse}", allCourse);
        QwenMessage qwenMessage = new QwenMessage();
        qwenMessage.setRole("user");
        qwenMessage.setContent(content);
        requestBody.setMessages(Collections.singletonList(qwenMessage));
        List<StudySchedule> schedules = AIAPIUtil.generateSchedules(JSONUtil.toJsonStr(requestBody));
        schedules.forEach(studySchedule -> {
            studySchedule.setUserId(userId);
            studySchedule.setTaskTime(new Date());
        });
        boolean b = studyScheduleService.saveBatch(schedules);
        if (!b) {
            RespBody.fail("bad MySQL");
        }
        return RespBody.data(scheduleReason, "生成成功");
    }

    @ApiOperation("获取外部资源")
    @PostMapping("/ai/xft/outer/resources")
    public RespBody<JSONArray> getOuterResources(@RequestBody OuterResourceRequestBody body) {
        MemberDto member = WebUtil.currentMember();
        if (member == null) {
            throw new BizException(GlobalRespCode.NO_LOGIN_ERROR);
        }
        String query = body.getQuery();
        if (query == null || query.length() > 1024) {
            throw new BizException(GlobalRespCode.PARAM_MISSED, "query不能为空或者长度超过1024");
        }
        final String REDIS_KEY = "outer:resources:" + query.hashCode();
        if (stringRedisTemplate.hasKey(REDIS_KEY)) {
            return RespBody.data(JSONUtil.parseArray(stringRedisTemplate.opsForValue().get(REDIS_KEY)));
        }
        String res = difyUtil.sendChatMessageSync(query, "xft-" + member.getId(), null);
        stringRedisTemplate.opsForValue().set(REDIS_KEY, res);
        return RespBody.data(JSONUtil.parseArray(res));
    }


    private String getReason(String schedulesTitleArrayStr) {
        QwenRequestBody re = getRequestBodyWithoutContentReplace(schedulesTitleArrayStr);
        String jsonStr = JSONUtil.toJsonStr(re);
        int hashCode = jsonStr.hashCode();
        final String redisKey = "schedule:reason:hash" + hashCode;
        // 存在就续期
        if (stringRedisTemplate.hasKey(redisKey)) {
            stringRedisTemplate.expire(redisKey, 1, TimeUnit.DAYS);
            return stringRedisTemplate.opsForValue().get(redisKey);
        }
        String scheduleReason = AIAPIUtil.getScheduleReason(jsonStr);
        stringRedisTemplate.opsForValue().set(redisKey, scheduleReason);
        Boolean expire = stringRedisTemplate.expire(redisKey, 1, TimeUnit.DAYS);
        if (!expire) {
            log.error("redis set schedule reason error");
        }
        return scheduleReason;
    }

    private QwenRequestBody getRequestBodyWithoutContentReplace(String schedules) {
        String content = castleConfigAiPromptService.getByEnum(AIAssiantEnum.ANALYZE_SCHEDULE).getContent();
        QwenRequestBody requestBody = new QwenRequestBody();
        requestBody.setModel(MODEL);
        QwenMessage qwenMessage = new QwenMessage();
        qwenMessage.setRole("user");
        qwenMessage.setContent(content.replace("#{schedules}", schedules));
        requestBody.setMessages(Collections.singletonList(qwenMessage));
        return requestBody;
    }
}
